﻿using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Reflection;
using System.Windows.Ria.Data;


namespace nisbus.Silverlight.Controls
{
    public partial class ReferenceDataForm : DataForm
    {
        #region DisplayPropertyName

        /// <summary>
        /// DisplayPropertyName Dependency Property
        /// </summary>
        public static readonly DependencyProperty DisplayPropertyNameProperty =
            DependencyProperty.Register("DisplayPropertyName", typeof(string), typeof(ReferenceDataForm),
                new PropertyMetadata((string)string.Empty, new PropertyChangedCallback(OnDisplayPropertyNameChanged)));                    

        /// <summary>
        /// Gets or sets the DisplayPropertyName property.
        /// </summary>
        public string DisplayPropertyName
        {
            get { return (string)GetValue(DisplayPropertyNameProperty); }
            set { SetValue(DisplayPropertyNameProperty, value); }
        }

        /// <summary>
        /// Handles changes to the DisplayPropertyName property.
        /// </summary>
        private static void OnDisplayPropertyNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ReferenceDataForm)d).OnDisplayPropertyNameChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the DisplayPropertyName property.
        /// </summary>
        protected virtual void OnDisplayPropertyNameChanged(DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region DomainDataSource

        /// <summary>
        /// DomainDataSource Dependency Property
        /// </summary>
        public static readonly DependencyProperty DomainDataSourceProperty =
            DependencyProperty.Register("DomainDataSource", typeof(DomainDataSource), typeof(ReferenceDataForm),
            new PropertyMetadata(new PropertyChangedCallback(OnDomainDataSourceChanged)));

        /// <summary>
        /// Gets or sets the DomainDataSource property.
        /// </summary>
        public DomainDataSource DomainDataSource
        {
            get { return (DomainDataSource)GetValue(DomainDataSourceProperty); }
            set
            {
                SetValue(DomainDataSourceProperty, value);
                this.Context = value.DomainContext;
                value.Loaded += delegate
                {
                    this.ItemsSource = value.Data;
                };
                contextMethods = this.Context.GetType().GetMethods();
            }
        }

        /// <summary>
        /// Handles changes to the DomainDataSource property.
        /// </summary>
        private static void OnDomainDataSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ReferenceDataForm)d).OnDomainDataSourceChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the DomainDataSource property.
        /// </summary>
        protected virtual void OnDomainDataSourceChanged(DependencyPropertyChangedEventArgs e)
        {

        }

        #endregion

        private DomainContext Context { get; set; }
        private MethodInfo[] contextMethods { get; set; }
        private Type CurrentType { get; set; }
        private Type ReferenceType { get; set; }

        public ReferenceDataForm()
        {
            InitializeComponent();
        }        

        protected override void OnItemEditEnded(DataFormEditEndedEventArgs e)
        {
            base.OnItemEditEnded(e);
            if (!this.ValidateItem())
            {
                var valid = this.ValidationSummary;
            }
        }

        protected override void OnContentLoaded(DataFormContentLoadEventArgs e)
        {
            base.OnContentLoaded(e);
            CreateReferences(e);
        }

        private void CreateReferences(DataFormContentLoadEventArgs e)
        {
            try
            {
                CurrentType = this.CurrentItem.GetType();
                PropertyInfo[] properties = CurrentType.GetProperties();
                //Get the fields of the current type
                var fields = CurrentType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
                //Check if the current type has any references
                var refs = fields.Where(x => x.FieldType.FullName.Contains("EntityRef")).ToList();
                if (refs != null)
                {
                    //Create the reference panel to add to the form
                    StackPanel referencePanel = ((StackPanel)e.Content).FindName("referencePanel") as StackPanel;
                    if (referencePanel == null)
                        referencePanel = new StackPanel { Name = "referencePanel" };
                    referencePanel.Children.Clear();


                    foreach (var referenceEntity in refs)
                    {
                        var referenceTypeName = referenceEntity.FieldType.FullName;
                        referenceTypeName = referenceTypeName.Substring(referenceTypeName.IndexOf("[[") + 2);
                        referenceTypeName = referenceTypeName.Substring(0, referenceTypeName.IndexOf(","));

                        //Get the property that matches the current reference item
                        var property = properties.FirstOrDefault(p => p.PropertyType.FullName == referenceTypeName);
                        if (property != null)
                        {
                            //Get the methods that return the referenced type
                            var method = contextMethods.FirstOrDefault(x => x.IsPublic && x.GetParameters().Count() == 0 && x.ReturnType.FullName.Contains("System.Windows.Ria.Data.EntityQuery`1[[" + property.PropertyType.FullName));
                            if (method != null)
                            {
                                string returnType = method.ReturnType.FullName;
                                returnType = returnType.Substring(returnType.IndexOf("[[") + 2);
                                returnType = returnType.Substring(0, returnType.IndexOf(","));

                                if (returnType == property.PropertyType.FullName)
                                {
                                    //Get the list of entities to populate the combobox
                                    var queryName = method.Name;
                                    DomainDataSource ds = new DomainDataSource();
                                    ds.DomainContext = Context;
                                    ds.QueryName = queryName;
                                    ds.Load();


                                    //Create the combobox and add it to the dataform
                                    ComboBox bx = new ComboBox { DisplayMemberPath = DisplayPropertyName, ItemsSource = ds.Data };
                                    referencePanel.Children.Insert(0, new DataField
                                    {
                                        Label = property.Name,
                                        Content = bx,
                                        IsRequired = true
                                    });


                                    //Catch the selection changed event and set the reference to the reference property on the entity
                                    bx.SelectionChanged += delegate(object cbx, SelectionChangedEventArgs selectArgs)
                                    {
                                        try
                                        {
                                            if (this.Mode == DataFormMode.AddNew)
                                            {
                                                bx.Dispatcher.BeginInvoke(new Action(() => { property.SetValue(this.CurrentItem, bx.SelectedItem, null); }));
                                            }
                                            else if (this.Mode == DataFormMode.Edit)
                                            {
                                                var actualValue = property.GetValue(this.CurrentItem, null);
                                                foreach (var item in bx.ItemsSource)
                                                {
                                                    if (item == actualValue)
                                                    {
                                                        bx.Dispatcher.BeginInvoke(new Action(() => { bx.SelectedItem = item; }));
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                        catch
                                        {
                                        }
                                    };
                                }
                            }
                        }
                    }
                    if (referencePanel.Children.Count > 0)
                        ((StackPanel)e.Content).Children.Insert(0, referencePanel);
                }
            }
            catch
            {
            }
        }

        protected override void OnAutoGeneratingField(DataFormAutoGeneratingFieldEventArgs e)
        {
            //Generate the fields as usual
            base.OnAutoGeneratingField(e);
            //Don't display read only fields
            e.Cancel = e.Field.IsReadOnly;
        }
    }
}
